home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / MDB / mssql.php < prev    next >
PHP Script  |  2004-03-24  |  52KB  |  1,412 lines

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
  7. // | Stig. S. Bakken, Lukas Smith, Frank M. Kromann                       |
  8. // | All rights reserved.                                                 |
  9. // +----------------------------------------------------------------------+
  10. // | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
  11. // | API as well as database abstraction for PHP applications.            |
  12. // | This LICENSE is in the BSD license style.                            |
  13. // |                                                                      |
  14. // | Redistribution and use in source and binary forms, with or without   |
  15. // | modification, are permitted provided that the following conditions   |
  16. // | are met:                                                             |
  17. // |                                                                      |
  18. // | Redistributions of source code must retain the above copyright       |
  19. // | notice, this list of conditions and the following disclaimer.        |
  20. // |                                                                      |
  21. // | Redistributions in binary form must reproduce the above copyright    |
  22. // | notice, this list of conditions and the following disclaimer in the  |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // |                                                                      |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission.                                                  |
  29. // |                                                                      |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  41. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Frank M. Kromann <frank@kromann.info>                        |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: mssql.php,v 1.6.4.20 2004/01/08 22:12:34 lsmith Exp $
  47. //
  48.  
  49. require_once('MDB/Common.php');
  50.  
  51. /**
  52.  * MDB MSSQL Server driver
  53.  *
  54.  * Notes:
  55.  * - Until at least version 6.5, the only kind of table changes that the
  56.  *   ALTER TABLE SQL statement of Microsoft SQL server supports is new field
  57.      and constraint additions.
  58.  *
  59.  * - The driver alterTable method does not implement table or column renaming,
  60.  *   column definitions changes or column dropping. In the future versions of
  61.  *   this driver those capabilities may be emulated using other SQL statements
  62.  *   to recreate the tables with a new definition.
  63.  *
  64.  * @package MDB
  65.  * @category Database
  66.  * @author  Frank M. Kromann <frank@kromann.info>
  67.  */
  68. class MDB_mssql extends MDB_Common
  69. {
  70.     // {{{ properties
  71.  
  72.     var $connection = 0;
  73.     var $connected_host;
  74.     var $connected_user;
  75.     var $connected_password;
  76.     var $connected_port;
  77.     var $opened_persistent = '';
  78.  
  79.     var $escape_quotes = "'";
  80.  
  81.     var $highest_fetched_row = array();
  82.     var $columns = array();
  83.  
  84.     // }}}
  85.     // {{{ constructor
  86.  
  87.     /**
  88.     * Constructor
  89.     */
  90.     function MDB_mssql()
  91.     {
  92.         $this->MDB_Common();
  93.         $this->phptype = 'mssql';
  94.         $this->dbsyntax = 'mssql';
  95.  
  96.         $this->supported['Sequences'] = 1;
  97.         $this->supported['Indexes'] = 1;
  98.         $this->supported['AffectedRows'] = 1;
  99.         $this->supported['Transactions'] = 1;
  100.         $this->supported['Summaryfunctions'] = 1;
  101.         $this->supported['OrderByText'] = 0;
  102.         $this->supported['CurrId'] = 1;
  103.         $this->supported['SelectRowRanges'] = 1;
  104.         $this->supported['LOBs'] = 1;
  105.         $this->supported['Replace'] = 1;
  106.         $this->supported['SubSelects'] = 1;
  107.  
  108.         $this->errorcode_map = array(
  109.             208   => MDB_ERROR_NOSUCHTABLE,
  110.             3701  => MDB_ERROR_NOSUCHTABLE
  111.         );
  112.     }
  113.  
  114.     // }}}
  115.     // {{{ errorCode()
  116.  
  117.     /**
  118.      * Get the native error code of the last error (if any) that
  119.      * occured on the current connection.
  120.      *
  121.      * @access public
  122.      *
  123.      * @return int native MSSQL error code
  124.      */
  125.     function errorCode()
  126.     {
  127.        $res = mssql_query('select @@ERROR as ErrorCode', $this->connection);
  128.        if (!$res) {
  129.            return MDB_ERROR;
  130.        }
  131.        $row = mssql_fetch_row($res);
  132.        if ($row[0] > 0) {
  133.            return $row[0];
  134.        }
  135.        else {
  136.            return null;
  137.        }
  138.     }
  139.  
  140.     // }}}
  141.     // {{{ mssqlRaiseError()
  142.  
  143.     /**
  144.      * This method is used to communicate an error and invoke error
  145.      * callbacks etc.  Basically a wrapper for MDB::raiseError
  146.      * that checks for native error msgs.
  147.      *
  148.      * @param integer $errno error code
  149.      * @return object a PEAR error object
  150.      * @access public
  151.      * @see PEAR_Error
  152.      */
  153.     function mssqlRaiseError($code = null)
  154.     {
  155.         $native_msg = mssql_get_last_message();
  156.         $native_code = $this->errorCode();
  157.         if ($code === null) {
  158.             if (isset($this->errorcode_map[$native_code])) {
  159.                 $code = $this->errorcode_map[$native_code];
  160.             } else {
  161.                 $code = MDB_ERROR;
  162.             }
  163.         }
  164.         return $this->raiseError($code, null, null, null, $native_code . ' - ' . $native_msg);
  165.     }
  166.  
  167.     // }}}
  168.     // {{{ autoCommit()
  169.  
  170.     /**
  171.      * Define whether database changes done on the database be automatically
  172.      * committed. This function may also implicitly start or end a transaction.
  173.      *
  174.      * @param boolean $auto_commit    flag that indicates whether the database
  175.      *                                changes should be committed right after
  176.      *                                executing every query statement. If this
  177.      *                                argument is 0 a transaction implicitly
  178.      *                                started. Otherwise, if a transaction is
  179.      *                                in progress it is ended by committing any
  180.      *                                database changes that were pending.
  181.      *
  182.      * @access public
  183.      *
  184.      * @return mixed MDB_OK on success, a MDB error on failure
  185.      */
  186.     function autoCommit($auto_commit)
  187.     {
  188.         $this->debug("AutoCommit: ".($auto_commit ? "On" : "Off"));
  189.         if (!isset($this->supported['Transactions'])) {
  190.             return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
  191.                 'Auto-commit transactions: transactions are not in use'));
  192.         }
  193.         if ($this->auto_commit == $auto_commit) {
  194.             return(MDB_OK);
  195.         }
  196.         if ($this->connection) {
  197.             if ($auto_commit) {
  198.                 $result = $this->query('COMMIT TRANSACTION');
  199.             } else {
  200.                 $result = $this->query('BEGIN TRANSACTION');
  201.             }
  202.             if (MDB::isError($result)) {
  203.                 return($result);
  204.             }
  205.         }
  206.         $this->auto_commit = $auto_commit;
  207.         $this->in_transaction = !$auto_commit;
  208.         return(MDB_OK);
  209.     }
  210.  
  211.     // }}}
  212.     // {{{ commit()
  213.  
  214.     /**
  215.      * Commit the database changes done during a transaction that is in
  216.      * progress. This function may only be called when auto-committing is
  217.      * disabled, otherwise it will fail. Therefore, a new transaction is
  218.      * implicitly started after committing the pending changes.
  219.      *
  220.      * @access public
  221.      *
  222.      * @return mixed MDB_OK on success, a MDB error on failure
  223.      */
  224.     function commit()
  225.     {
  226.         $this->debug("Commit Transaction");
  227.         if (!isset($this->supported['Transactions'])) {
  228.             return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
  229.                 'Commit transactions: transactions are not in use'));
  230.         }
  231.         if ($this->auto_commit) {
  232.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  233.             'Commit transactions: transaction changes are being auto commited'));
  234.         }
  235.         $result = $this->query('COMMIT TRANSACTION');
  236.         if (MDB::isError($result)) {
  237.             return($result);
  238.         }
  239.         return($this->query('BEGIN TRANSACTION'));
  240.     }
  241.  
  242.     // }}}
  243.     // {{{ rollback()
  244.  
  245.     /**
  246.      * Cancel any database changes done during a transaction that is in
  247.      * progress. This function may only be called when auto-committing is
  248.      * disabled, otherwise it will fail. Therefore, a new transaction is
  249.      * implicitly started after canceling the pending changes.
  250.      *
  251.      * @access public
  252.      *
  253.      * @return mixed MDB_OK on success, a MDB error on failure
  254.      */
  255.     function rollback()
  256.     {
  257.         $this->debug("Rollback Transaction");
  258.         if (!isset($this->supported['Transactions'])) {
  259.             return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
  260.                 'Rollback transactions: transactions are not in use'));
  261.         }
  262.         if ($this->auto_commit) {
  263.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  264.                 'Rollback transactions: transactions can not be rolled back when changes are auto commited'));
  265.         }
  266.         $result = $this->query('ROLLBACK TRANSACTION');
  267.         if (MDB::isError($result)) {
  268.             return($result);
  269.         }
  270.         return($this->query('BEGIN TRANSACTION'));
  271.     }
  272.  
  273.     function _doQuery($query)
  274.     {
  275.         $this->current_row = $this->affected_rows = -1;
  276.         return(@mssql_query($query, $this->connection));
  277.     }
  278.  
  279.     // }}}
  280.     // {{{ connect()
  281.  
  282.     /**
  283.      * Connect to the database
  284.      *
  285.      * @return TRUE on success, MDB_Error on failure
  286.      **/
  287.     function connect()
  288.     {
  289.         $port = (isset($this->port) ? $this->port : '');
  290.         if($this->connection != 0) {
  291.             if (!strcmp($this->connected_host, $this->host)
  292.                 && !strcmp($this->connected_user, $this->user)
  293.                 && !strcmp($this->connected_password, $this->password)
  294.                 && !strcmp($this->connected_port, $port)
  295.                 && $this->opened_persistent == $this->options['persistent'])
  296.             {
  297.                 return(MDB_OK);
  298.             }
  299.             mssql_close($this->connection);
  300.             $this->connection = 0;
  301.             $this->affected_rows = -1;
  302.         }
  303.  
  304.         if(!PEAR::loadExtension($this->phptype)) {
  305.             return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
  306.                 NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
  307.                 'MDB_Error', TRUE));
  308.         }
  309.  
  310.         $function = ($this->options['persistent'] ? 'mssql_pconnect' : 'mssql_connect');
  311.         if (!function_exists($function)) {
  312.             return($this->raiseError(MDB_ERROR_UNSUPPORTED));
  313.         }
  314.  
  315.         @ini_set('track_errors', TRUE);
  316.         $this->connection = @$function(
  317.             $this->host.(!strcmp($port,'') ? '' : ':'.$port),
  318.             $this->user, $this->password);
  319.         @ini_restore('track_errors');
  320.         if ($this->connection <= 0) {
  321.             return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL,
  322.                 $php_errormsg));
  323.         }
  324.  
  325.         if(isset($this->supported['Transactions']) && !$this->auto_commit
  326.             && !$this->_doQuery("BEGIN TRANSACTION"))
  327.         {
  328.             mssql_close($this->connection);
  329.             $this->connection = 0;
  330.             $this->affected_rows = -1;
  331.             return($this->raiseError("Connect: Could not begin the initial transaction"));
  332.         }
  333.         $this->connected_host = $this->host;
  334.         $this->connected_user = $this->user;
  335.         $this->connected_password = $this->password;
  336.         $this->connected_port = $port;
  337.         $this->selected_database = $this->database_name;
  338.         $this->opened_persistent = $this->getoption('persistent');
  339.         return(MDB_OK);
  340.     }
  341.  
  342.     // }}}
  343.     // {{{ _close()
  344.     /**
  345.      * all the RDBMS specific things needed close a DB connection
  346.      *
  347.      * @return boolean
  348.      * @access private
  349.      **/
  350.     function _close()
  351.     {
  352.         if ($this->connection != 0) {
  353.             if (isset($this->supported['Transactions']) && !$this->auto_commit) {
  354.                 $result = $this->_doQuery("ROLLBACK TRANSACTION");
  355.             }
  356.             mssql_close($this->connection);
  357.             $this->connection = 0;
  358.             $this->affected_rows = $this->current_row = -1;
  359.  
  360.             if (isset($result) && MDB::isError($result)) {
  361.                 return($result);
  362.             }
  363.             unset($GLOBALS['_MDB_databases'][$this->database]);
  364.             return(TRUE);
  365.         }
  366.         return(FALSE);
  367.     }
  368.  
  369.     function standaloneQuery($query)
  370.     {
  371.         if(!PEAR::loadExtension($this->phptype)) {
  372.             return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
  373.                 NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
  374.                 'MDB_Error', TRUE));
  375.         }
  376.         $connection = mssql_connect($this->host,$this->user,$this->password);
  377.         if($connection == 0) {
  378.             return($this->mssqlRaiseError("Query: Could not connect to the Microsoft SQL server"));
  379.         }
  380.         $result = @mssql_query($query, $connection);
  381.         if(!$result) {
  382.             return($this->mssqlRaiseError("Query: Could not query a Microsoft SQL server"));
  383.         }
  384.         mssql_close($connection);
  385.         return(MDB_OK);
  386.     }
  387.  
  388.     // }}}
  389.     // {{{ query()
  390.  
  391.     /**
  392.      * Send a query to the database and return any results
  393.      *
  394.      * @access public
  395.      *
  396.      * @param string  $query  the SQL query
  397.      * @param mixed   $types  array that contains the types of the columns in
  398.      *                        the result set
  399.      *
  400.      * @return mixed a result handle or MDB_OK on success, a MDB error on failure
  401.      */
  402.     function query($query, $types = NULL)
  403.     {
  404.         $this->debug("Query: $query");
  405.         if ($this->database_name) {
  406.             $ismanip = MDB::isManip($query);
  407.             $this->last_query = $query;
  408.             $first = $this->first_selected_row;
  409.             $limit = $this->selected_row_limit;
  410.             $this->first_selected_row = $this->selected_row_limit = 0;
  411.  
  412.             $last_connection = $this->connection;
  413.             $result = $this->connect();
  414.             if (MDB::isError($result)) {
  415.                 return($result);
  416.             }
  417.             if($limit > 0) {
  418.                 $fetch = $first + $limit;
  419.                 if (!$ismanip) {
  420.                     $query = str_replace('SELECT', "SELECT TOP $fetch", $query);
  421.                 }
  422.             }
  423.             if( $last_connection != $this->connection
  424.                 || !strcmp($this->selected_database, '')
  425.                 || strcmp($this->selected_database, $this->database_name))
  426.             {
  427.                 if(!mssql_select_db($this->database_name, $this->connection)) {
  428.                     return($this->mssqlRaiseError());
  429.                 }
  430.             }
  431.             if ($result = $this->_doQuery($query)) {
  432.                 if ($ismanip) {
  433.                     $this->affected_rows = mssql_rows_affected($this->connection);
  434.                     return(MDB_OK);
  435.                 } else {
  436.                     if($first > 0 || $limit > 0) {
  437.                         $this->limits[$result] = array($first, $limit);
  438.                     }
  439.                     $this->highest_fetched_row[$result] = -1;
  440.                     if ($types != NULL) {
  441.                         if (!is_array($types)) {
  442.                             $types = array($types);
  443.                         }
  444.                         $err = $this->setResultTypes($result, $types);
  445.                         if (MDB::isError($err)) {
  446.                             $this->freeResult($result);
  447.                             return($err);
  448.                         }
  449.                     }
  450.                     return($result);
  451.                 }
  452.             }
  453.         }
  454.  
  455.         return($this->mssqlRaiseError());
  456.     }
  457.  
  458.     // }}}
  459.     // {{{ getColumnNames()
  460.  
  461.     /**
  462.      * Retrieve the names of columns returned by the DBMS in a query result.
  463.      *
  464.      * @param resource   $result    result identifier
  465.      * @return mixed                an associative array variable
  466.      *                              that will hold the names of columns. The
  467.      *                              indexes of the array are the column names
  468.      *                              mapped to lower case and the values are the
  469.      *                              respective numbers of the columns starting
  470.      *                              from 0. Some DBMS may not return any
  471.      *                              columns when the result set does not
  472.      *                              contain any rows.
  473.      *
  474.      *                              a MDB error on failure
  475.      * @access public
  476.      */
  477.     function getColumnNames($result)
  478.     {
  479.         $result_value = intval($result);
  480.         if (!isset($this->highest_fetched_row[$result_value])) {
  481.             return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
  482.                 'Get column names: it was specified an inexisting result set'));
  483.         }
  484.         if (!isset($this->columns[$result_value])) {
  485.             $this->columns[$result_value] = array();
  486.             $columns = mssql_num_fields($result);
  487.             for($column = 0; $column < $columns; $column++) {
  488.                 $this->columns[$result_value][strtolower(mssql_field_name($result, $column))] = $column;
  489.             }
  490.         }
  491.         return($this->columns[$result_value]);
  492.     }
  493.  
  494.     // }}}
  495.     // {{{ numCols()
  496.  
  497.     /**
  498.      * Count the number of columns returned by the DBMS in a query result.
  499.      *
  500.      * @param resource    $result        result identifier
  501.      * @access public
  502.      * @return mixed integer value with the number of columns, a MDB error
  503.      *                       on failure
  504.      */
  505.     function numCols($result)
  506.     {
  507.         if (!isset($this->highest_fetched_row[intval($result)])) {
  508.             return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
  509.                 'numCols: it was specified an inexisting result set'));
  510.         }
  511.         return(mssql_num_fields($result));
  512.     }
  513.  
  514.     // }}}
  515.     // {{{ endOfResult()
  516.  
  517.     /**
  518.     * check if the end of the result set has been reached
  519.     *
  520.     * @param resource    $result result identifier
  521.     * @return mixed TRUE or FALSE on sucess, a MDB error on failure
  522.     * @access public
  523.     */
  524.     function endOfResult($result)
  525.     {
  526.         if (!isset($this->highest_fetched_row[$result])) {
  527.             return($this->raiseError(MDB_ERROR, NULL, NULL,
  528.                 'End of result: attempted to check the end of an unknown result'));
  529.         }
  530.         return($this->highest_fetched_row[$result] >= $this->numRows($result)-1);
  531.     }
  532.  
  533.     // }}}
  534.     // {{{ fetch()
  535.  
  536.     /**
  537.     * fetch value from a result set
  538.     *
  539.     * @param resource    $result result identifier
  540.     * @param int    $row    number of the row where the data can be found
  541.     * @param int    $field    field number where the data can be found
  542.     * @return mixed string on success, a MDB error on failure
  543.     * @access public
  544.     */
  545.     function fetch($result, $row, $field)
  546.     {
  547.         $this->highest_fetched_row[$result] = max($this->highest_fetched_row[$result], $row);
  548.         if (isset($this->limits[$result])) {
  549.             $row += $this->limits[$result][0];
  550.         }
  551.         $res = @mssql_result($result, $row, $field);
  552.         if ($res === FALSE && $res != NULL) {
  553.             return($this->mssqlRaiseError($errno));
  554.         }
  555.         return($res);
  556.     }
  557.  
  558.     // }}}
  559.     // {{{ fetchClob()
  560.  
  561.     /**
  562.     * fetch a clob value from a result set
  563.     *
  564.     * @param resource    $result result identifier
  565.     * @param int    $row    number of the row where the data can be found
  566.     * @param int    $field    field number where the data can be found
  567.     * @return mixed content of the specified data cell, a MDB error on failure,
  568.     *               a MDB error on failure
  569.     * @access public
  570.     */
  571.     function fetchClob($result, $row, $field)
  572.     {
  573.         return($this->fetchLob($result, $row, $field));
  574.     }
  575.  
  576.     // }}}
  577.     // {{{ fetchBlob()
  578.  
  579.     /**
  580.     * fetch a blob value from a result set
  581.     *
  582.     * @param resource    $result result identifier
  583.     * @param int    $row    number of the row where the data can be found
  584.     * @param int    $field    field number where the data can be found
  585.     * @return mixed content of the specified data cell, a MDB error on failure
  586.     * @access public
  587.     */
  588.     function fetchBlob($result, $row, $field)
  589.     {
  590.         return($this->fetchLob($result, $row, $field));
  591.     }
  592.  
  593.     // }}}
  594.     // {{{ convertResult()
  595.  
  596.     /**
  597.     * convert a value to a RDBMS indepdenant MDB type
  598.     *
  599.     * @param mixed  $value   value to be converted
  600.     * @param int    $type    constant that specifies which type to convert to
  601.     * @return mixed converted value
  602.     * @access public
  603.     */
  604.     function convertResult($value, $type)
  605.     {
  606.         switch($type) {
  607.             case MDB_TYPE_BOOLEAN:
  608.                 return ($value == '1') ? TRUE : FALSE;
  609.             case MDB_TYPE_DATE:
  610.                 if(strlen($value) > 10) {
  611.                     $value=substr($value,0,10);
  612.                 }
  613.                 return($value);
  614.             case MDB_TYPE_TIME:
  615.                 if(strlen($value) > 8) {
  616.                     $value=substr($value,11,8);
  617.                 }
  618.                 return($value);
  619.             case MDB_TYPE_TIMESTAMP:
  620.                 return($value);
  621.             default:
  622.                 return($this->_baseConvertResult($value,$type));
  623.         }
  624.     }
  625.  
  626.     // }}}
  627.     // {{{ numRows()
  628.  
  629.     /**
  630.     * returns the number of rows in a result object
  631.     *
  632.     * @param ressource $result a valid result ressouce pointer
  633.     * @return mixed MDB_Error or the number of rows
  634.     * @access public
  635.     */
  636.     function numRows($result)
  637.     {
  638.         $rows = mssql_num_rows($result);
  639.         if (isset($this->limits[$result])) {
  640.             $rows -= $this->limits[$result][0];
  641.             if ($rows < 0) $rows = 0;
  642.         }
  643.         return($rows);
  644.     }
  645.  
  646.     // }}}
  647.     // {{{ freeResult()
  648.  
  649.     /**
  650.      * Free the internal resources associated with $result.
  651.      *
  652.      * @param $result result identifier
  653.      * @return boolean TRUE on success, FALSE if $result is invalid
  654.      * @access public
  655.      */
  656.     function freeResult($result)
  657.     {
  658.         if(isset($this->fetched_row[$result])) {
  659.             unset($this->fetched_row[$result]);
  660.         }
  661.         if(isset($this->highest_fetched_row[$result])) {
  662.             unset($this->highest_fetched_row[$result]);
  663.         }
  664.         if(isset($this->columns[$result])) {
  665.             unset($this->columns[$result]);
  666.         }
  667.         if(isset($this->result_types[$result])) {
  668.             unset($this->result_types[$result]);
  669.         }
  670.         return(mssql_free_result($result));
  671.     }
  672.  
  673.     // }}}
  674.     // {{{ getIntegerDeclaration()
  675.  
  676.     /**
  677.      * Obtain DBMS specific SQL code portion needed to declare an integer type
  678.      * field to be used in statements like CREATE TABLE.
  679.      *
  680.      * @param string  $name   name the field to be declared.
  681.      * @param string  $field  associative array with the name of the properties
  682.      *                        of the field being declared as array indexes.
  683.      *                        Currently, the types of supported field
  684.      *                        properties are as follows:
  685.      *
  686.      *                       unsigned
  687.      *                        Boolean flag that indicates whether the field
  688.      *                        should be declared as unsigned integer if
  689.      *                        possible.
  690.      *
  691.      *                       default
  692.      *                        Integer value to be used as default for this
  693.      *                        field.
  694.      *
  695.      *                       notnull
  696.      *                        Boolean flag that indicates whether this field is
  697.      *                        constrained to not be set to NULL.
  698.      * @return string  DBMS specific SQL code portion that should be used to
  699.      *                 declare the specified field.
  700.      * @access public
  701.      */
  702.     function getIntegerDeclaration($name, $field)
  703.     {
  704.         if (isset($field['unsigned'])) {
  705.             $this->warnings[] = "unsigned integer field \"$name\" is being
  706.                 declared as signed integer";
  707.         }
  708.         return("$name INT".(isset($field["default"]) ? " DEFAULT ".$field["default"] : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  709.     }
  710.  
  711.     // }}}
  712.     // {{{ getTextDeclaration()
  713.  
  714.     /**
  715.      * Obtain DBMS specific SQL code portion needed to declare an text type
  716.      * field to be used in statements like CREATE TABLE.
  717.      *
  718.      * @param string $name name the field to be declared.
  719.      * @param string $field associative array with the name of the properties
  720.      *       of the field being declared as array indexes. Currently, the types
  721.      *       of supported field properties are as follows:
  722.      *
  723.      *       length
  724.      *           Integer value that determines the maximum length of the text
  725.      *           field. If this argument is missing the field should be
  726.      *           declared to have the longest length allowed by the DBMS.
  727.      *
  728.      *       default
  729.      *           Text value to be used as default for this field.
  730.      *
  731.      *       notnull
  732.      *           Boolean flag that indicates whether this field is constrained
  733.      *           to not be set to NULL.
  734.      * @return string DBMS specific SQL code portion that should be used to
  735.      *       declare the specified field.
  736.      * @access public
  737.      */
  738.     function getTextDeclaration($name, $field)
  739.     {
  740.         return((isset($field["length"]) ? "$name VARCHAR (".$field["length"].")" : "$name TEXT").(isset($field["default"]) ? " DEFAULT ".$this->GetTextValue($field["default"]) : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  741.     }
  742.  
  743.     // }}}
  744.     // {{{ getClobDeclaration()
  745.  
  746.     /**
  747.      * Obtain DBMS specific SQL code portion needed to declare an character
  748.      * large object type field to be used in statements like CREATE TABLE.
  749.      *
  750.      * @param string  $name   name the field to be declared.
  751.      * @param string  $field  associative array with the name of the
  752.      *                        properties of the field being declared as array
  753.      *                        indexes. Currently, the types of supported field
  754.      *                        properties are as follows:
  755.      *
  756.      *                       length
  757.      *                        Integer value that determines the maximum length
  758.      *                        of the large object field. If this argument is
  759.      *                        missing the field should be declared to have the
  760.      *                        longest length allowed by the DBMS.
  761.      *
  762.      *                       notnull
  763.      *                        Boolean flag that indicates whether this field
  764.      *                        is constrained to not be set to NULL.
  765.      * @return string  DBMS specific SQL code portion that should be used to
  766.      *                 declare the specified field.
  767.      * @access public
  768.      */
  769.     function getClobDeclaration($name, $field)
  770.     {
  771.         if (isset($field["length"])) {
  772.             $length = $field["length"];
  773.             if ($length <= 8000) {
  774.                 $type = "VARCHAR($length)";
  775.             } else {
  776.                 $type = "TEXT";
  777.             }
  778.         } else {
  779.             $type = "TEXT";
  780.         }
  781.         return("$name $type".(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  782.     }
  783.  
  784.     // }}}
  785.     // {{{ getBlobDeclaration()
  786.  
  787.     /**
  788.      * Obtain DBMS specific SQL code portion needed to declare an binary large
  789.      * object type field to be used in statements like CREATE TABLE.
  790.      *
  791.      * @param string  $name   name the field to be declared.
  792.      * @param string  $field  associative array with the name of the properties
  793.      *                        of the field being declared as array indexes.
  794.      *                        Currently, the types of supported field
  795.      *                        properties are as follows:
  796.      *
  797.      *                       length
  798.      *                        Integer value that determines the maximum length
  799.      *                        of the large object field. If this argument is
  800.      *                        missing the field should be declared to have the
  801.      *                        longest length allowed by the DBMS.
  802.      *
  803.      *                       notnull
  804.      *                        Boolean flag that indicates whether this field is
  805.      *                        constrained to not be set to NULL.
  806.      * @return string  DBMS specific SQL code portion that should be used to
  807.      *                 declare the specified field.
  808.      * @access public
  809.      */
  810.     function getBlobDeclaration($name, $field)
  811.     {
  812.         if(isset($field["length"])) {
  813.             $length = $field["length"];
  814.             if($length <= 8000) {
  815.                 $type = "VARBINARY($length)";
  816.             } else {
  817.                 $type = "IMAGE";
  818.             }
  819.         } else {
  820.             $type = "IMAGE";
  821.         }
  822.         return("$name $type".(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  823.     }
  824.  
  825.     // }}}
  826.     // {{{ getBooleanDeclaration()
  827.  
  828.     /**
  829.      * Obtain DBMS specific SQL code portion needed to declare a boolean type
  830.      * field to be used in statements like CREATE TABLE.
  831.      *
  832.      * @param string $name name the field to be declared.
  833.      * @param string $field associative array with the name of the properties
  834.      *       of the field being declared as array indexes. Currently, the types
  835.      *       of supported field properties are as follows:
  836.      *
  837.      *       default
  838.      *           Boolean value to be used as default for this field.
  839.      *
  840.      *       notnullL
  841.      *           Boolean flag that indicates whether this field is constrained
  842.      *           to not be set to NULL.
  843.      * @return string DBMS specific SQL code portion that should be used to
  844.      *       declare the specified field.
  845.      * @access public
  846.      */
  847.     function getBooleanDeclaration($name, $field)
  848.     {
  849.         return("$name BIT".(isset($field["default"]) ? " DEFAULT ".$field["default"] : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  850.     }
  851.  
  852.     // }}}
  853.     // {{{ getDateDeclaration()
  854.  
  855.     /**
  856.      * Obtain DBMS specific SQL code portion needed to declare an date type
  857.      * field to be used in statements like CREATE TABLE.
  858.      *
  859.      * @param string  $name   name the field to be declared.
  860.      * @param string  $field  associative array with the name of the properties
  861.      *                        of the field being declared as array indexes.
  862.      *                        Currently, the types of supported field properties
  863.      *                        are as follows:
  864.      *
  865.      *                       default
  866.      *                        Date value to be used as default for this field.
  867.      *
  868.      *                       notnull
  869.      *                        Boolean flag that indicates whether this field is
  870.      *                        constrained to not be set to NULL.
  871.      * @return string  DBMS specific SQL code portion that should be used to
  872.      *                 declare the specified field.
  873.      * @access public
  874.      */
  875.     function getDateDeclaration($name, $field)
  876.     {
  877.         return("$name CHAR (".strlen("YYYY-MM-DD").")".(isset($field["default"]) ? " DEFAULT ".$this->getDateValue($field["default"]) : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  878.     }
  879.  
  880.     // }}}
  881.     // {{{ getTimestampDeclaration()
  882.  
  883.     /**
  884.      * Obtain DBMS specific SQL code portion needed to declare an timestamp
  885.      * type field to be used in statements like CREATE TABLE.
  886.      *
  887.      * @param string  $name   name the field to be declared.
  888.      * @param string  $field  associative array with the name of the properties
  889.      *                        of the field being declared as array indexes.
  890.      *                        Currently, the types of supported field
  891.      *                        properties are as follows:
  892.      *
  893.      *                       default
  894.      *                        Time stamp value to be used as default for this
  895.      *                        field.
  896.      *
  897.      *                       notnull
  898.      *                        Boolean flag that indicates whether this field is
  899.      *                        constrained to not be set to NULL.
  900.      * @return string  DBMS specific SQL code portion that should be used to
  901.      *                 declare the specified field.
  902.      * @access public
  903.      */
  904.     function getTimestampDeclaration($name, $field)
  905.     {
  906.         return("$name CHAR (".strlen("YYYY-MM-DD HH:MM:SS").")".(isset($field["default"]) ? " DEFAULT ".$this->getTimestampValue($field["default"]) : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  907.     }
  908.  
  909.     // }}}
  910.     // {{{ getTimeDeclaration()
  911.  
  912.     /**
  913.      * Obtain DBMS specific SQL code portion needed to declare an time type
  914.      * field to be used in statements like CREATE TABLE.
  915.      *
  916.      * @param string  $name   name the field to be declared.
  917.      * @param string  $field  associative array with the name of the properties
  918.      *                        of the field being declared as array indexes.
  919.      *                        Currently, the types of supported field
  920.      *                        properties are as follows:
  921.      *
  922.      *                       default
  923.      *                        Time value to be used as default for this field.
  924.      *
  925.      *                       notnull
  926.      *                        Boolean flag that indicates whether this field is
  927.      *                        constrained to not be set to NULL.
  928.      * @return string  DBMS specific SQL code portion that should be used to
  929.      *                 declare the specified field.
  930.      * @access public
  931.      */
  932.     function getTimeDeclaration($name, $field)
  933.     {
  934.         return("$name CHAR (".strlen("HH:MM:SS").")".(isset($field["default"]) ? " DEFAULT ".$this->getTimeValue($field["default"]) : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  935.     }
  936.  
  937.     // }}}
  938.     // {{{ getFloatDeclaration()
  939.  
  940.     /**
  941.      * Obtain DBMS specific SQL code portion needed to declare an float type
  942.      * field to be used in statements like CREATE TABLE.
  943.      *
  944.      * @param string  $name   name the field to be declared.
  945.      * @param string  $field  associative array with the name of the properties
  946.      *                        of the field being declared as array indexes.
  947.      *                        Currently, the types of supported field
  948.      *                        properties are as follows:
  949.      *
  950.      *                       default
  951.      *                        Integer value to be used as default for this
  952.      *                        field.
  953.      *
  954.      *                       notnull
  955.      *                        Boolean flag that indicates whether this field is
  956.      *                        constrained to not be set to NULL.
  957.      * @return string  DBMS specific SQL code portion that should be used to
  958.      *                 declare the specified field.
  959.      * @access public
  960.      */
  961.     function getFloatDeclaration($name, $field)
  962.     {
  963.         return("$name FLOAT".(isset($field["default"]) ? " DEFAULT ".$field["default"] : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  964.     }
  965.  
  966.     // }}}
  967.     // {{{ getDecimalDeclaration()
  968.  
  969.     /**
  970.      * Obtain DBMS specific SQL code portion needed to declare an decimal type
  971.      * field to be used in statements like CREATE TABLE.
  972.      *
  973.      * @param string  $name   name the field to be declared.
  974.      * @param string  $field  associative array with the name of the properties
  975.      *                        of the field being declared as array indexes.
  976.      *                        Currently, the types of supported field
  977.      *                        properties are as follows:
  978.      *
  979.      *                       default
  980.      *                        Integer value to be used as default for this
  981.      *                        field.
  982.      *
  983.      *                       notnull
  984.      *                        Boolean flag that indicates whether this field is
  985.      *                        constrained to not be set to NULL.
  986.      * @return string  DBMS specific SQL code portion that should be used to
  987.      *                 declare the specified field.
  988.      * @access public
  989.      */
  990.     function getDecimalDeclaration($name, $field)
  991.     {
  992.         return("$name DECIMAL(18,".$this->decimal_places.")".(isset($field["default"]) ? " DEFAULT ".$this->getDecimalValue($field["default"]) : "").(isset($field["notnull"]) ? " NOT NULL" : " NULL"));
  993.     }
  994.  
  995.     // }}}
  996.     // {{{ getClobValue()
  997.  
  998.     /**
  999.      * Convert a text value into a DBMS specific format that is suitable to
  1000.      * compose query statements.
  1001.      *
  1002.      * @param resource  $prepared_query query handle from prepare()
  1003.      * @param           $parameter
  1004.      * @param           $clob
  1005.      * @return string  text string that represents the given argument value in
  1006.      *                 a DBMS specific format.
  1007.      * @access public
  1008.      */
  1009.     function getClobValue($prepared_query, $parameter, $clob)
  1010.     {
  1011.         $value="'";
  1012.         while(!$this->endOfLob($clob)) {
  1013.             if (MDB::isError($result = $this->readLob($clob, $data, $this->options['lob_buffer_length']))) {
  1014.                 return($result);
  1015.             }
  1016.             $value .= $this->_quote($data);
  1017.         }
  1018.         $value .= "'";
  1019.         return($value);
  1020.     }
  1021.  
  1022.     // }}}
  1023.     // {{{ freeClobValue()
  1024.  
  1025.     /**
  1026.      * free a character large object
  1027.      *
  1028.      * @param resource  $prepared_query query handle from prepare()
  1029.      * @param string    $clob
  1030.      * @return MDB_OK
  1031.      * @access public
  1032.      */
  1033.     function freeClobValue($prepared_query, $clob)
  1034.     {
  1035.         unset($this->lobs[$clob]);
  1036.         return(MDB_OK);
  1037.     }
  1038.  
  1039.     // }}}
  1040.     // {{{ getBlobValue()
  1041.  
  1042.     /**
  1043.      * Convert a text value into a DBMS specific format that is suitable to
  1044.      * compose query statements.
  1045.      *
  1046.      * @param resource  $prepared_query query handle from prepare()
  1047.      * @param           $parameter
  1048.      * @param           $blob
  1049.      * @return string  text string that represents the given argument value in
  1050.      *                 a DBMS specific format.
  1051.      * @access public
  1052.      */
  1053.     function getBlobValue($prepared_query, $parameter, $blob)
  1054.     {
  1055.         $value = "0x";
  1056.         while(!$this->endOfLob($blob))
  1057.         {
  1058.             if (MDB::isError($result = $this->readLob($blob, $data, $this->options['lob_buffer_length']))) {
  1059.                 return($result);
  1060.             }
  1061.             $value.= Bin2Hex($data);
  1062.         }
  1063.         return($value);
  1064.     }
  1065.  
  1066.     // }}}
  1067.     // {{{ freeBlobValue()
  1068.  
  1069.     /**
  1070.      * free a binary large object
  1071.      *
  1072.      * @param resource  $prepared_query query handle from prepare()
  1073.      * @param string    $blob
  1074.      * @return MDB_OK
  1075.      * @access public
  1076.      */
  1077.     function freeBlobValue($prepared_query, $blob)
  1078.     {
  1079.         unset($this->lobs[$blob]);
  1080.         return(MDB_OK);
  1081.     }
  1082.  
  1083.     // }}}
  1084.     // {{{ getBooleanValue()
  1085.  
  1086.     /**
  1087.      * Convert a text value into a DBMS specific format that is suitable to
  1088.      * compose query statements.
  1089.      *
  1090.      * @param string $value text string value that is intended to be converted.
  1091.      * @return string text string that represents the given argument value in
  1092.      *       a DBMS specific format.
  1093.      * @access public
  1094.      */
  1095.     function getBooleanValue($value)
  1096.     {
  1097.         return(($value === NULL) ? 'NULL' : $value);
  1098.     }
  1099.  
  1100.     // }}}
  1101.     // {{{ getFloatValue()
  1102.  
  1103.     /**
  1104.      * Convert a text value into a DBMS specific format that is suitable to
  1105.      * compose query statements.
  1106.      *
  1107.      * @param string  $value text string value that is intended to be converted.
  1108.      * @return string  text string that represents the given argument value in
  1109.      *                 a DBMS specific format.
  1110.      * @access public
  1111.      */
  1112.     function getFloatValue($value)
  1113.     {
  1114.         return(($value === NULL) ? 'NULL' : $value);
  1115.     }
  1116.  
  1117.     // }}}
  1118.     // {{{ getDecimalValue()
  1119.  
  1120.     /**
  1121.      * Convert a text value into a DBMS specific format that is suitable to
  1122.      * compose query statements.
  1123.      *
  1124.      * @param string  $value text string value that is intended to be converted.
  1125.      * @return string  text string that represents the given argument value in
  1126.      *                 a DBMS specific format.
  1127.      * @access public
  1128.      */
  1129.     function getDecimalValue($value)
  1130.     {
  1131.         return(($value === NULL) ? 'NULL' : $value);
  1132.     }
  1133.  
  1134.     // }}}
  1135.     // {{{ nextId()
  1136.  
  1137.     /**
  1138.      * returns the next free id of a sequence
  1139.      *
  1140.      * @param string  $seq_name name of the sequence
  1141.      * @param boolean $ondemand when true the seqence is
  1142.      *                          automatic created, if it
  1143.      *                          not exists
  1144.      *
  1145.      * @return mixed MDB_Error or id
  1146.      * @access public
  1147.      */
  1148.     function nextId($seq_name, $ondemand = TRUE)
  1149.     {
  1150.         $sequence_name = $this->getSequenceName($seq_name);
  1151.         $this->expectError(MDB_ERROR_NOSUCHTABLE);
  1152.         $result = $this->query("INSERT INTO $sequence_name DEFAULT VALUES");
  1153.         $this->popExpect();
  1154.         if ($ondemand && MDB::isError($result) &&
  1155.             $result->getCode() == MDB_ERROR_NOSUCHTABLE)
  1156.         {
  1157.             // Since we are creating the sequence on demand
  1158.             // we know the first id = 1 so initialize the
  1159.             // sequence at 2
  1160.             $result = $this->createSequence($seq_name, 2);
  1161.             if (MDB::isError($result)) {
  1162.                 return($this->raiseError(MDB_ERROR, NULL, NULL,
  1163.                     'Next ID: on demand sequence could not be created'));
  1164.             } else {
  1165.                 // First ID of a newly created sequence is 1
  1166.                 return(1);
  1167.             }
  1168.         }
  1169.         $value = $this->queryOne("SELECT @@IDENTITY FROM $sequence_name", 'integer');
  1170.         if (MDB::isError($value)) {
  1171.             return($value);
  1172.         }
  1173.         $result = $this->query("DELETE FROM $sequence_name WHERE sequence < $value");
  1174.         if (MDB::isError($result)) {
  1175.             $this->warnings[] = 'nextID: could not delete previous sequence table values';
  1176.         }
  1177.         return($value);
  1178.     }
  1179.  
  1180.     // }}}
  1181.     // {{{ currId()
  1182.  
  1183.     /**
  1184.      * returns the current id of a sequence
  1185.      *
  1186.      * @param string  $seq_name name of the sequence
  1187.      * @return mixed MDB_Error or id
  1188.      * @access public
  1189.      */
  1190.     function currId($seq_name)
  1191.     {
  1192.         $sequence_name = $this->getSequenceName($seq_name);
  1193.         $result = $this->query("SELECT MAX(sequence) FROM $sequence_name", 'integer');
  1194.         if (MDB::isError($result)) {
  1195.             return($result);
  1196.         }
  1197.  
  1198.         return($this->fetchOne($result));
  1199.     }
  1200.  
  1201.     // }}}
  1202.     // {{{ fetchInto()
  1203.  
  1204.     /**
  1205.      * Fetch a row and insert the data into an existing array.
  1206.      *
  1207.      * @param resource  $result     result identifier
  1208.      * @param int       $fetchmode  how the array data should be indexed
  1209.      * @param int       $rownum     the row number to fetch
  1210.      * @return int data array on success, a MDB error on failure
  1211.      * @access public
  1212.      */
  1213.     function fetchInto($result, $fetchmode = MDB_FETCHMODE_DEFAULT, $rownum = NULL)
  1214.     {
  1215.         if (is_null($rownum)) {
  1216.             ++$this->highest_fetched_row[$result];
  1217.         } else {
  1218.             $this->highest_fetched_row[$result] =
  1219.                 max($this->highest_fetched_row[$result], $rownum);
  1220.             if (isset($this->limits[$result])) {
  1221.                 $rownum = $rownum + $this->limits[$result][0];
  1222.             }
  1223.             if (!@mssql_data_seek($result, $rownum)) {
  1224.                 return(NULL);
  1225.             }
  1226.         }
  1227.         if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
  1228.             $fetchmode = $this->fetchmode;
  1229.         }
  1230.         if ($fetchmode & MDB_FETCHMODE_ASSOC) {
  1231.             $row = @mssql_fetch_assoc($result);
  1232.             if (is_array($row) && $this->options['optimize'] == 'portability') {
  1233.                 $row = array_change_key_case($row, CASE_LOWER);
  1234.             }
  1235.         } else {
  1236.             $row = @mssql_fetch_row($result);
  1237.         }
  1238.         if (!$row) {
  1239.             if($this->options['autofree']) {
  1240.                 $this->freeResult($result);
  1241.             }
  1242.             return(NULL);
  1243.         }
  1244.         if (isset($this->result_types[$result])) {
  1245.             $row = $this->convertResultRow($result, $row);
  1246.         }
  1247.         return($row);
  1248.     }
  1249.  
  1250.     // }}}
  1251.     // {{{ tableInfo()
  1252.  
  1253.   /**
  1254.      * Returns information about a table or a result set
  1255.      *
  1256.      * NOTE: doesn't support table name and flags if called from a db_result
  1257.      *
  1258.      * @param  mixed $resource SQL Server result identifier or table name
  1259.      * @param  int $mode A valid tableInfo mode (MDB_TABLEINFO_ORDERTABLE or
  1260.      *                   MDB_TABLEINFO_ORDER)
  1261.      *
  1262.      * @return array An array with all the information
  1263.      */
  1264.     function tableInfo($result, $mode = NULL)
  1265.     {
  1266.  
  1267.         $count = 0;
  1268.         $id    = 0;
  1269.         $res   = array();
  1270.  
  1271.         /*
  1272.          * depending on $mode, metadata returns the following values:
  1273.          *
  1274.          * - mode is false (default):
  1275.          * $result[]:
  1276.          *   [0]['table']  table name
  1277.          *   [0]['name']   field name
  1278.          *   [0]['type']   field type
  1279.          *   [0]['len']    field length
  1280.          *   [0]['flags']  field flags
  1281.          *
  1282.          * - mode is MDB_TABLEINFO_ORDER
  1283.          * $result[]:
  1284.          *   ["num_fields"] number of metadata records
  1285.          *   [0]['table']  table name
  1286.          *   [0]['name']   field name
  1287.          *   [0]['type']   field type
  1288.          *   [0]['len']    field length
  1289.          *   [0]['flags']  field flags
  1290.          *   ['order'][field name]  index of field named "field name"
  1291.          *   The last one is used, if you have a field name, but no index.
  1292.          *   Test:  if (isset($result['meta']['myfield'])) { ...
  1293.          *
  1294.          * - mode is MDB_TABLEINFO_ORDERTABLE
  1295.          *    the same as above. but additionally
  1296.          *   ["ordertable"][table name][field name] index of field
  1297.          *      named "field name"
  1298.          *
  1299.          *      this is, because if you have fields from different
  1300.          *      tables with the same field name * they override each
  1301.          *      other with MDB_TABLEINFO_ORDER
  1302.          *
  1303.          *      you can combine MDB_TABLEINFO_ORDER and
  1304.          *      MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
  1305.          *      MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
  1306.          */
  1307.  
  1308.         // if $result is a string, then we want information about a
  1309.         // table without a resultset
  1310.  
  1311.         if (is_string($result)) {
  1312.             if (!@mssql_select_db($this->database_name, $this->connection)) {
  1313.                 return $this->mssqlRaiseError(MDB_ERROR_NODBSELECTED);
  1314.             }
  1315.             $id = mssql_query("SELECT * FROM $result", $this->connection);
  1316.             if (empty($id)) {
  1317.                 return($this->mssqlRaiseError());
  1318.             }
  1319.         } else { // else we want information about a resultset
  1320.             $id = $result;
  1321.             if (empty($id)) {
  1322.                 return($this->mssqlRaiseError());
  1323.             }
  1324.         }
  1325.  
  1326.         $count = @mssql_num_fields($id);
  1327.  
  1328.         // made this IF due to performance (one if is faster than $count if's)
  1329.         if (empty($mode)) {
  1330.  
  1331.             for ($i=0; $i<$count; $i++) {
  1332.                 $res[$i]['table'] = (is_string($result)) ? $result : '';
  1333.                 $res[$i]['name']  = @mssql_field_name($id, $i);
  1334.                 $res[$i]['type']  = @mssql_field_type($id, $i);
  1335.                 $res[$i]['len']   = @mssql_field_length($id, $i);
  1336.                 // We only support flags for tables
  1337.                 $res[$i]['flags'] = is_string($result) ? $this->_mssql_field_flags($result, $res[$i]['name']) : '';
  1338.             }
  1339.  
  1340.         } else { // full
  1341.             $res['num_fields']= $count;
  1342.  
  1343.             for ($i=0; $i<$count; $i++) {
  1344.                 $res[$i]['table'] = (is_string($result)) ? $result : '';
  1345.                 $res[$i]['name']  = @mssql_field_name($id, $i);
  1346.                 $res[$i]['type']  = @mssql_field_type($id, $i);
  1347.                 $res[$i]['len']   = @mssql_field_length($id, $i);
  1348.                 // We only support flags for tables
  1349.                 $res[$i]['flags'] = is_string($result) ? $this->_mssql_field_flags($result, $res[$i]['name']) : '';
  1350.                 if ($mode & MDB_TABLEINFO_ORDER) {
  1351.                     $res['order'][$res[$i]['name']] = $i;
  1352.                 }
  1353.                 if ($mode & MDB_TABLEINFO_ORDERTABLE) {
  1354.                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  1355.                 }
  1356.             }
  1357.         }
  1358.  
  1359.         // free the result only if we were called on a table
  1360.         if (is_string($result)) {
  1361.             @mssql_free_result($id);
  1362.         }
  1363.         return($res);
  1364.     }
  1365.  
  1366.     // }}}
  1367.     // {{{ _mssql_field_flags()
  1368.     /**
  1369.     * Get the flags for a field, currently only supports "isnullable" and "primary_key"
  1370.     *
  1371.     * @param string The table name
  1372.     * @param string The field
  1373.     * @access private
  1374.     */
  1375.     function _mssql_field_flags($table, $column)
  1376.     {
  1377.         static $current_table = NULL;
  1378.         static $flags;
  1379.         // At the first call we discover the flags for all fields
  1380.         if ($table != $current_table) {
  1381.             $flags = array();
  1382.             // find nullable fields
  1383.             $q_nulls = "SELECT syscolumns.name, syscolumns.isnullable
  1384.                         FROM sysobjects
  1385.                         INNER JOIN syscolumns ON sysobjects.id = syscolumns.id
  1386.                         WHERE sysobjects.name ='$table' AND syscolumns.isnullable = 1";
  1387.             $res = $this->query($q_nulls, NULL, FALSE);
  1388.             $res = $this->fetchAll($res, MDB_FETCHMODE_ASSOC);
  1389.             foreach ($res as $data) {
  1390.                 if ($data['isnullable'] == 1) {
  1391.                     $flags[$data['name']][] = 'isnullable';
  1392.                 }
  1393.             }
  1394.             // find primary keys
  1395.             $res2 = $this->query("EXEC SP_PKEYS[$table]", NULL, FALSE);
  1396.             $res2 = $this->fetchAll($res, MDB_FETCHMODE_ASSOC);
  1397.             foreach ($res2 as $data) {
  1398.                 if (!empty($data['COLUMN_NAME'])) {
  1399.                     $flags[$data['COLUMN_NAME']][] = 'primary_key';
  1400.                 }
  1401.             }
  1402.             $current_table = $table;
  1403.         }
  1404.         if (isset($flags[$column])) {
  1405.             return(implode(',', $flags[$column]));
  1406.         }
  1407.         return('');
  1408.     }
  1409.     // }}}
  1410. }
  1411.  
  1412. ?>